/*
 * Copyright (c) 2009, The MilkyTracker Team.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - Neither the name of the <ORGANIZATION> nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *  LoaderMDL.cpp
 *  MilkyPlay Module Loader: Digitracker 3
 *  --------------------------------
 *			Version History:
 *  --------------------------------
 *  01/16/05: Added loader for version 0.0 (no joke)
 *  ??/??/04: Make it working with platform independent file loading routines
 *  ??/??/98: First version of this MDL loader
 */
#include "Loaders.h"

struct tmdlins
{
    mp_ubyte sampnum;
    mp_ubyte playrangeend;
    mp_ubyte vol;
    mp_ubyte volenv;
    mp_ubyte pan;
    mp_ubyte panenv;
    mp_uint32 volfade;
    mp_ubyte vibspeed;
    mp_ubyte vibdepth;
    mp_ubyte vibsweep;
    mp_ubyte vibform;
    mp_ubyte reserved;
    mp_ubyte freqenv;
};

struct tmdlsamp
{
    mp_ubyte sampnum;
    mp_ubyte smpname[32];
    mp_ubyte filename[8];
    mp_uint32 c4spd;
    mp_uint32 samplen;
    mp_uint32 loopstart;
    mp_uint32 looplen;
    mp_ubyte reserved;
    mp_ubyte infobyte;
    mp_sbyte* smp;
};

static inline mp_uint32 cvol(mp_ubyte vol)
{
    return (((mp_sint32)(vol - 1)) * 65795) >> 16;
}

static inline mp_uint32 cpan(mp_ubyte pan)
{
    return (((mp_uint32)pan) * 131589) >> 16;
}

const char* LoaderMDL::identifyModule(const mp_ubyte* buffer)
{
    // check for .MDL module
    if (!memcmp(buffer, "DMDL", 4))
    {
        return "MDL";
    }

    return NULL;
}

mp_sint32 LoaderMDL::load(XMFileBase& f, XModule* module)
{
    mp_uint32 i, e;

    mp_ubyte dummy[256];

    module->cleanUp();

    TXMHeader* header = &module->header;
    TXMInstrument* instr = module->instr;
    TXMSample* smp = module->smp;
    TXMPattern* phead = module->phead;

    // we're already out of memory here
    if (!phead || !instr || !smp)
        return -7;

    mp_ubyte sig[5];
    f.read(&sig, 1, 5);

    if (memcmp(sig, "DMDL", 4))
    {
        return -8;
    }

    mp_ubyte hVer = sig[4] >> 4;

    if (hVer > 1)
    {
        return -8;
    }

    memcpy(header->sig, sig, 4);
    strcpy(header->tracker, "Digitracker 3");

    mp_uint32 numtracks = 0;
    mp_uint32 numins = 0;
    mp_uint32 numsamples = 0;
    mp_ubyte* tracks = NULL;

    // waste some memory
    tmdlins* mdlins = new tmdlins[16 * 256];
    tmdlsamp* mdlsamp = new tmdlsamp[256];
    mp_uint32* trackseq = new mp_uint32[256 * 32];

    if (mdlins == NULL || mdlsamp == NULL || trackseq == NULL)
    {
        if (mdlins) delete[] mdlins;
        if (mdlsamp) delete[] mdlsamp;
        if (trackseq) delete[] trackseq;

        return -7;
    }

    memset(mdlins, 0, sizeof(tmdlins) * 16 * 256);
    memset(mdlsamp, 0, sizeof(tmdlsamp) * 256);
    memset(trackseq, 0, 256 * 32 * 4);

    header->ver = sig[4];

    mp_ubyte blockhead[6];

    while (f.read(&blockhead, 1, 6))
    {

        mp_ubyte id[3];
        memcpy(id, blockhead, 2);
        id[2] = 0;
        mp_uint32 blocklen = LittleEndian::GET_DWORD(blockhead + 2);

        if (!memcmp(&blockhead, "IN", 2))
        {
            f.read(&header->name, 1, 32);
            f.read(&dummy, 1, 20);
            f.readWords(&header->ordnum, 1);
            f.readWords(&header->restart, 1);
            header->mainvol = f.readByte();
            header->tempo = f.readByte();
            header->speed = f.readByte();
            f.read(&dummy, 1, 32);
            mp_uint32 c = 32;
            while ((dummy[c - 1] & 128)) c--;
            header->channum = c;

            for (i = 0; i < c; i++) header->pan[i] = cpan(dummy[i] & 127);

            f.read(&header->ord, 1, header->ordnum);
            for (i = 0; i < c; i++)
                f.read(&dummy, 1, 8);
        }
        else if (!memcmp(&blockhead, "PA", 2))
        {
            header->patnum = f.readByte();
            for (mp_sint32 p = 0; p < header->patnum; p++)
            {
                mp_ubyte numch = 0;
                mp_sint32 numrows = 0;

                if (hVer == 1)
                {
                    numch = f.readByte();
                    numrows = f.readByte();
                    f.read(&dummy, 1, 16);
                }
                else if (hVer == 0)
                {
                    numch = 32;
                    numrows = 63;
                }

                for (mp_sint32 c = 0; c < numch; c++)
                {
                    mp_uint32 tr = 0;
                    tr = f.readWord();
                    trackseq[p * 32 + c] = tr;
                }

                //if ((mp_uword)numch>header->channum) numch=(char)header->channum;
                numch = (mp_ubyte)header->channum;

                numrows++;

                phead[p].rows = numrows;
                phead[p].effnum = 3;
                phead[p].channum = numch < (mp_ubyte)header->channum ? numch : (mp_ubyte)header->channum;
                phead[p].patternData = new mp_ubyte[phead[p].rows * phead[p].channum * 8];

                // out of memory?
                if (phead[p].patternData == NULL)
                {
                    if (mdlins) delete[] mdlins;
                    if (mdlsamp) delete[] mdlsamp;
                    if (trackseq) delete[] trackseq;

                    return -7;
                }

                memset(phead[p].patternData, 0, phead[p].rows * phead[p].channum * 8);

            }
        }
        else if (!memcmp(&blockhead, "TR", 2))
        {
            mp_ubyte* buffer = new mp_ubyte[2048];
            numtracks = f.readWord();
            tracks = new mp_ubyte[(mp_uint32)numtracks * 256 * 6];

            if (buffer == NULL || tracks == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (buffer) delete[] buffer;
                if (tracks) delete[] tracks;

                return -7;
            }

            for (mp_uint32 t = 0; t < numtracks; t++)
            {
                mp_uint32 tracksize = f.readWord();
                memset(buffer, 0, 2048);
                f.read(buffer, 1, tracksize);
                //tracks[t]=new mp_ubyte[256*6];

                memset(tracks + t * 256 * 6, 0, 256 * 6);

                mp_ubyte* track = tracks + t * 256 * 6;
                //mp_ubyte *track = tracks[t];

                mp_uint32 d = 0, p = 0;
                while (d < tracksize)
                {
                    mp_ubyte b = buffer[d++];
                    mp_uint32 pb = b & 3;
                    mp_uint32 op = b >> 2;

                    switch (pb)
                    {
                        case 0: {
                            for (i = 0; i < (op + 1); i++)
                            {
                                memset(track + (p * 6), 0, 6);
                                p++;
                            }
                            break;
                        };
                        case 1: {
                            mp_sint32 lp = p - 1;
                            for (i = 0; i < (op + 1); i++)
                            {
                                memcpy(track + (p * 6), track + (lp * 6), 6);
                                p++;
                            }
                            break;
                        };
                        case 2: {
                            memcpy(track + (p * 6), track + (op * 6), 6);
                            p++;
                            break;
                        };
                        case 3: {
                            if ((op & 1)) track[p * 6] = buffer[d++];
                            if ((op & 2)) track[p * 6 + 1] = buffer[d++];
                            if ((op & 4)) track[p * 6 + 2] = buffer[d++];
                            if ((op & 8)) track[p * 6 + 3] = buffer[d++];
                            if ((op & 16)) track[p * 6 + 4] = buffer[d++];
                            if ((op & 32)) track[p * 6 + 5] = buffer[d++];
                            p++;
                            break;
                        };
                    }
                }

                // something went wrong here
                if (d != tracksize)
                {
                    if (mdlins) delete[] mdlins;
                    if (mdlsamp) delete[] mdlsamp;
                    if (trackseq) delete[] trackseq;
                    if (buffer) delete[] buffer;
                    if (tracks) delete[] tracks;

                    return -8;
                }

            }
            delete[] buffer;
        }
        else if (!memcmp(&blockhead, "II", 2))
        {
            mp_uint32 insnum = 0, numsamples = 0;
            numins = f.readByte();
            for (i = 0; i < numins; i++)
            {
                insnum = f.readByte();
                numsamples = f.readByte();

                if (!insnum)
                {
                    if (mdlins) delete[] mdlins;
                    if (mdlsamp) delete[] mdlsamp;
                    if (trackseq) delete[] trackseq;

                    return -8;
                }

                f.read(&instr[insnum - 1].name, 1, 32);

                instr[insnum - 1].samp = numsamples;

                for (mp_uint32 s = 0; s < numsamples; s++)
                {
                    mdlins[(insnum - 1) * 16 + s].sampnum = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].playrangeend = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].vol = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].volenv = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].pan = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].panenv = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].volfade = f.readWord();
                    mdlins[(insnum - 1) * 16 + s].vibspeed = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].vibspeed = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].vibsweep = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].vibform = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].reserved = f.readByte();
                    mdlins[(insnum - 1) * 16 + s].freqenv = f.readByte();
                }
            }
            header->insnum = insnum;
        }
        else if (!memcmp(&blockhead, "IS", 2))
        {
            numsamples = f.readByte();
            for (mp_uint32 s = 0; s < numsamples; s++)
            {
                mdlsamp[s].sampnum = f.readByte();
                f.read(&mdlsamp[s].smpname, 1, 32);

                f.read(&mdlsamp[s].filename, 1, 8);

                if (hVer == 1)
                    mdlsamp[s].c4spd = f.readDword();
                else if (hVer == 0)
                    mdlsamp[s].c4spd = f.readWord();

                mdlsamp[s].samplen = f.readDword();
                mdlsamp[s].loopstart = f.readDword();
                mdlsamp[s].looplen = f.readDword();
                mdlsamp[s].reserved = f.readByte();
                mdlsamp[s].infobyte = f.readByte();
            }
        }
        else if (!memcmp(&blockhead, "SA", 2))
        {
            for (mp_uint32 s = 0; s < numsamples; s++)
            {

                mp_ubyte pb = (mdlsamp[s].infobyte >> 2) & 3;

                switch (pb)
                {
                    case 0: {
                        if (!(mdlsamp[s].infobyte & 1))
                        {
                            mdlsamp[s].smp = (mp_sbyte*)module->allocSampleMem(mdlsamp[s].samplen);

                            // out of memory
                            if (mdlsamp[s].smp == NULL)
                            {
                                if (mdlins) delete[] mdlins;
                                if (mdlsamp) delete[] mdlsamp;
                                if (trackseq) delete[] trackseq;
                                if (tracks) delete[] tracks;

                                return -7;
                            }

                            if (!module->loadSample(f, mdlsamp[s].smp, mdlsamp[s].samplen, mdlsamp[s].samplen))
                            {
                                if (mdlins) delete[] mdlins;
                                if (mdlsamp) delete[] mdlsamp;
                                if (trackseq) delete[] trackseq;
                                if (tracks) delete[] tracks;

                                return -7;
                            }

                        }
                        else
                        {
                            mdlsamp[s].smp = (mp_sbyte*)module->allocSampleMem(mdlsamp[s].samplen);

                            // out of memory
                            if (mdlsamp[s].smp == NULL)
                            {
                                if (mdlins) delete[] mdlins;
                                if (mdlsamp) delete[] mdlsamp;
                                if (trackseq) delete[] trackseq;
                                if (tracks) delete[] tracks;

                                return -7;
                            }

                            if (!module->loadSample(f, mdlsamp[s].smp, mdlsamp[s].samplen, mdlsamp[s].samplen >> 1, XModule::ST_16BIT))
                            {
                                if (mdlins) delete[] mdlins;
                                if (mdlsamp) delete[] mdlsamp;
                                if (trackseq) delete[] trackseq;
                                if (tracks) delete[] tracks;

                                return -7;
                            }

                            //mp_uint32 samplen = mdlsamp[s].samplen>>1;
                            //mp_uint32 loopstart = mdlsamp[s].loopstart>>1;
                            //mp_uint32 looplen = mdlsamp[s].looplen>>1;
                        }
                    }; break;
                    case 1: {
                        mp_sint32 size = (mp_sint32)f.readDword();

                        mdlsamp[s].smp = (mp_sbyte*)module->allocSampleMem(mdlsamp[s].samplen);

                        // out of memory
                        if (mdlsamp[s].smp == NULL)
                        {
                            if (mdlins) delete[] mdlins;
                            if (mdlsamp) delete[] mdlsamp;
                            if (trackseq) delete[] trackseq;
                            if (tracks) delete[] tracks;

                            return -7;
                        }

                        if (!module->loadSample(f, mdlsamp[s].smp, size, mdlsamp[s].samplen, XModule::ST_PACKING_MDL))
                        {
                            if (mdlins) delete[] mdlins;
                            if (mdlsamp) delete[] mdlsamp;
                            if (trackseq) delete[] trackseq;
                            if (tracks) delete[] tracks;

                            return -7;
                        }

                    }; break;
                    case 2: {
                        mp_sint32 size = (mp_sint32)f.readDword();

                        mdlsamp[s].smp = (mp_sbyte*)module->allocSampleMem(mdlsamp[s].samplen);

                        // out of memory
                        if (mdlsamp[s].smp == NULL)
                        {
                            if (mdlins) delete[] mdlins;
                            if (mdlsamp) delete[] mdlsamp;
                            if (trackseq) delete[] trackseq;
                            if (tracks) delete[] tracks;

                            return -7;
                        }

                        mp_uint32 samplen = mdlsamp[s].samplen >> 1;
                        //mp_uint32 loopstart = mdlsamp[s].loopstart>>1;
                        //mp_uint32 looplen = mdlsamp[s].looplen>>1;

                        if (!module->loadSample(f, mdlsamp[s].smp, size, samplen, XModule::ST_PACKING_MDL | XModule::ST_16BIT))
                        {
                            if (mdlins) delete[] mdlins;
                            if (mdlsamp) delete[] mdlsamp;
                            if (trackseq) delete[] trackseq;
                            if (tracks) delete[] tracks;

                            return -7;
                        }

                    }; break;
                }
            }
        }
        else if (!memcmp(&blockhead, "VE", 2))
        {
            mp_uint32 numenvs = f.readByte();

            mp_ubyte* envelopes = new mp_ubyte[numenvs * 33];

            // out of memory
            if (envelopes == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }

            f.read(envelopes, 33, numenvs);

            mp_ubyte* env = envelopes;

            mp_ubyte num;
            mp_uint32 envnum = 0;

            mp_uint32 lastEnv = 0;
            for (e = 0; e < numenvs; e++)
            {
                envnum = env[e * 33];
                if (envnum > lastEnv)
                    lastEnv = envnum;
            }

            module->venvs = new TEnvelope[lastEnv + 1];
            if (module->venvs == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }
            memset(module->venvs, 0, sizeof(TEnvelope) * (lastEnv + 1));
            module->numVEnvsAlloc = lastEnv + 1;
            module->numVEnvs = lastEnv + 1;

            for (e = 0; e < numenvs; e++)
            {
                envnum = env[0];

                ASSERT(envnum < lastEnv + 1);

                num = 0;
                mp_uint32 x = 0;
                mp_uint32 y;
                while (env[((mp_uint32)num * 2) + 1] && (num < 15))
                {
                    x += env[((mp_uint32)num * 2) + 1];
                    y = (((mp_uint32)env[((mp_uint32)num * 2) + 2]) * 266306) >> 16;
                    module->venvs[envnum].env[num][0] = x - 1;
                    module->venvs[envnum].env[num][1] = y;
                    num++;
                }
                module->venvs[envnum].num = num;
                module->venvs[envnum].sustain = env[31] & 0xf;
                module->venvs[envnum].loops = env[32] & 0xf;
                module->venvs[envnum].loope = env[32] >> 4;

                module->venvs[envnum].type = 1;
                if ((env[31] & 16)) module->venvs[envnum].type |= 2;
                if ((env[31] & 32)) module->venvs[envnum].type |= 4;

                env += 33;
            }

            header->volenvnum = envnum + 1;

            delete[] envelopes;

        }
        else if (!memcmp(&blockhead, "PE", 2))
        {
            mp_uint32 numenvs = f.readByte();

            mp_ubyte* envelopes = new mp_ubyte[numenvs * 33];

            // out of memory
            if (envelopes == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }

            f.read(envelopes, 33, numenvs);

            mp_ubyte* env = envelopes;

            mp_ubyte num;
            mp_uint32 envnum = 0;

            mp_uint32 lastEnv = 0;
            for (e = 0; e < numenvs; e++)
            {
                envnum = env[e * 33];
                if (envnum > lastEnv)
                    lastEnv = envnum;
            }

            module->penvs = new TEnvelope[lastEnv + 1];
            if (module->penvs == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }
            memset(module->penvs, 0, sizeof(TEnvelope) * (lastEnv + 1));
            module->numPEnvsAlloc = lastEnv + 1;
            module->numPEnvs = lastEnv + 1;

            for (e = 0; e < numenvs; e++)
            {
                envnum = env[0];

                ASSERT(envnum < lastEnv + 1);

                num = 0;
                mp_uint32 x = 0;
                mp_uint32 y;
                while (env[((mp_uint32)num * 2) + 1] && (num < 15))
                {
                    x += env[((mp_uint32)num * 2) + 1];
                    y = (((mp_uint32)env[((mp_uint32)num * 2) + 2]) * 266306) >> 16;
                    module->penvs[envnum].env[num][0] = x - 1;
                    module->penvs[envnum].env[num][1] = y;
                    num++;
                }
                module->penvs[envnum].num = num;
                module->penvs[envnum].sustain = env[31] & 0xf;
                module->penvs[envnum].loops = env[32] & 0xf;
                module->penvs[envnum].loope = env[32] >> 4;

                module->penvs[envnum].type = 1;
                if ((env[31] & 16)) module->penvs[envnum].type |= 2;
                if ((env[31] & 32)) module->penvs[envnum].type |= 4;

                env += 33;
            }

            header->panenvnum = envnum + 1;

            delete[] envelopes;

        }
        else if (!memcmp(&blockhead, "FE", 2))
        {
            mp_uint32 numenvs = f.readByte();

            mp_ubyte* envelopes = new mp_ubyte[numenvs * 33];

            // out of memory
            if (envelopes == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }

            f.read(envelopes, 33, numenvs);

            mp_ubyte* env = envelopes;

            mp_ubyte num;
            mp_uint32 envnum = 0;

            mp_uint32 lastEnv = 0;
            for (e = 0; e < numenvs; e++)
            {
                envnum = env[e * 33];
                if (envnum > lastEnv)
                    lastEnv = envnum;
            }

            module->fenvs = new TEnvelope[lastEnv + 1];
            if (module->fenvs == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
            }
            memset(module->fenvs, 0, sizeof(TEnvelope) * (lastEnv + 1));
            module->numFEnvsAlloc = lastEnv + 1;
            module->numFEnvs = lastEnv + 1;

            for (e = 0; e < numenvs; e++)
            {
                envnum = env[0];

                ASSERT(envnum < lastEnv + 1);

                num = 0;
                mp_uint32 x = 0;
                mp_uint32 y;
                while (env[((mp_uint32)num * 2) + 1] && (num < 15))
                {
                    x += env[((mp_uint32)num * 2) + 1];
                    y = (((mp_uint32)env[((mp_uint32)num * 2) + 2]) * 266306) >> 16;
                    module->fenvs[envnum].env[num][0] = x - 1;
                    module->fenvs[envnum].env[num][1] = y;
                    num++;
                }
                module->fenvs[envnum].num = num;
                module->fenvs[envnum].sustain = env[31] & 0xf;
                module->fenvs[envnum].loops = env[32] & 0xf;
                module->fenvs[envnum].loope = env[32] >> 4;

                module->fenvs[envnum].type = 1;
                if ((env[31] & 16)) module->fenvs[envnum].type |= 2;
                if ((env[31] & 32)) module->fenvs[envnum].type |= 4;

                env += 33;
            }

            header->frqenvnum = envnum + 1;

            delete[] envelopes;

        }
        else if (!memcmp(&blockhead, "ME", 2))
        {

            // MDL doc says song message is always 0 terminated,
            // well allocate one more byte to make sure it's really, really 0-terminated
            module->allocateSongMessage(blocklen + 1);

            if (module->message == NULL)
            {
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;
                return -7;
            }

            // read song message
            f.read(module->message, 1, blocklen);
        }
        else
        {
            /*mp_ubyte *buffer = new mp_ubyte[blocklen];

               if (buffer == NULL)
               {
                // out of memory
                if (mdlins) delete[] mdlins;
                if (mdlsamp) delete[] mdlsamp;
                if (trackseq) delete[] trackseq;
                if (tracks) delete[] tracks;

                return -7;
               }

               f.read(buffer,1,blocklen);

               delete[] buffer;*/

            f.seekWithBaseOffset(f.posWithBaseOffset() + blocklen);
        }

    }

    // ---------------------------------------------------------------
    //                   build new song structure
    // ---------------------------------------------------------------

    if (hVer == 1)
    {

        // create static envelope for samples samples without volume envelope
        TEnvelope venv;

        //venvs[header->volenvnum].type=5;
        //venvs[header->volenvnum].num=2;
        //venvs[header->volenvnum].loops=0;
        //venvs[header->volenvnum].loope=1;
        //venvs[header->volenvnum].env[0][0]=0;
        //venvs[header->volenvnum].env[0][1]=256;
        //venvs[header->volenvnum].env[1][0]=128;
        //venvs[header->volenvnum].env[1][1]=256;

        venv.type = 5;
        venv.num = 2;
        venv.loops = 0;
        venv.loope = 1;
        venv.env[0][0] = 0;
        venv.env[0][1] = 256;
        venv.env[1][0] = 128;
        venv.env[1][1] = 256;

        if (!module->addVolumeEnvelope(venv))
        {
            if (mdlins) delete[] mdlins;
            if (mdlsamp) delete[] mdlsamp;
            if (trackseq) delete[] trackseq;
            if (tracks) delete[] tracks;

            return -7;
        }

        header->volenvnum++;

        mp_uint32 sampcnt = 0;
        for (i = 0; i < header->insnum; i++)
        {
            //cprintf("%i\r\n",instr[i].samp);
            if (instr[i].samp)
            {
                mp_sint32 bs = 0;
                for (mp_uint32 s = 0; s < instr[i].samp; s++)
                {
                    //cprintf("%i, %i\r\n",bs,mdlins[i*16+s].playrangeend);
                    //getch();
                    mp_uint32 l;
                    for (l = bs; l <= mdlins[i * 16 + s].playrangeend; l++) instr[i].snum[l] = sampcnt;
                    bs = l;
                    for (l = 0; l < numsamples; l++)
                    {
                        if (mdlins[i * 16 + s].sampnum == mdlsamp[l].sampnum)
                        {
                            smp[sampcnt].samplen = mdlsamp[l].samplen;
                            smp[sampcnt].loopstart = mdlsamp[l].loopstart;
                            smp[sampcnt].looplen = mdlsamp[l].looplen;

                            if ((mdlins[i * 16 + s].volenv & 64)) smp[sampcnt].flags |= 1;
                            if ((mdlins[i * 16 + s].panenv & 64)) smp[sampcnt].flags |= 2;

                            if ((mdlins[i * 16 + s].volenv & 128)) smp[sampcnt].venvnum = (mdlins[i * 16 + s].volenv & 63) + 1;
                            else smp[sampcnt].venvnum = header->volenvnum;
                            if ((mdlins[i * 16 + s].panenv & 128)) smp[sampcnt].penvnum = (mdlins[i * 16 + s].panenv & 63) + 1;
                            if ((mdlins[i * 16 + s].freqenv & 128)) smp[sampcnt].fenvnum = (mdlins[i * 16 + s].freqenv & 63) + 1;

                            smp[sampcnt].vol = cvol(mdlins[i * 16 + s].vol);
                            XModule::convertc4spd(mdlsamp[l].c4spd, &smp[sampcnt].finetune, &smp[sampcnt].relnote);
                            if (mdlsamp[l].looplen && (mdlsamp[l].infobyte & 2)) smp[sampcnt].type = 2;
                            else if (mdlsamp[l].looplen && (!(mdlsamp[l].infobyte & 2))) smp[sampcnt].type = 1;

                            if ((mdlsamp[l].infobyte & 1))
                            {
                                smp[sampcnt].samplen >>= 1;
                                smp[sampcnt].loopstart >>= 1;
                                smp[sampcnt].looplen >>= 1;
                                smp[sampcnt].type |= 16;
                            }

                            smp[sampcnt].pan = cpan(mdlins[i * 16 + s].pan);
                            smp[sampcnt].vibtype = mdlins[i * 16 + s].vibform;
                            smp[sampcnt].vibsweep = mdlins[i * 16 + s].vibsweep;
                            smp[sampcnt].vibdepth = mdlins[i * 16 + s].vibdepth;
                            smp[sampcnt].vibrate = mdlins[i * 16 + s].vibspeed;
                            smp[sampcnt].volfade = mdlins[i * 16 + s].volfade;
                            smp[sampcnt].sample = mdlsamp[l].smp;
                            memcpy(&smp[sampcnt].name, &mdlsamp[l].smpname, 32);

                            sampcnt++;
                            continue;
                        }
                    }
                }
            }
        }

        header->smpnum = sampcnt;

    }
    else if (hVer == 0)
    {

        header->insnum = 0;

        for (mp_uint32 s = 0; s < numsamples; s++)
        {
            i = mdlsamp[s].sampnum - 1;

            if ((i + 1) > header->insnum)
                header->insnum = i + 1;

            instr[i].samp = 1;

            for (mp_sint32 j = 0; j < 120; j++)
                instr[i].snum[j] = s;

            memcpy(instr[i].name, mdlsamp[s].smpname, 32);
            memcpy(smp[s].name, mdlsamp[s].filename, 8);

            XModule::convertc4spd(mdlsamp[s].c4spd, &smp[s].finetune, &smp[s].relnote);

            smp[s].vol = cvol(mdlsamp[s].reserved);

            smp[s].flags = 1;

            smp[s].samplen = mdlsamp[s].samplen;
            smp[s].loopstart = mdlsamp[s].loopstart;
            smp[s].looplen = mdlsamp[s].looplen;

            if (mdlsamp[s].looplen && (mdlsamp[s].infobyte & 2)) smp[s].type = 2;
            else if (mdlsamp[s].looplen && (!(mdlsamp[s].infobyte & 2))) smp[s].type = 1;

            if ((mdlsamp[s].infobyte & 1))
            {
                smp[s].samplen >>= 1;
                smp[s].loopstart >>= 1;
                smp[s].looplen >>= 1;
                smp[s].type |= 16;
            }

            smp[s].sample = mdlsamp[s].smp;

            /*mdlsamp[s].samplen = f.readDword();
               mdlsamp[s].loopstart = f.readDword();
               mdlsamp[s].looplen = f.readDword();
               mdlsamp[s].reserved = f.readByte();
               mdlsamp[s].infobyte = f.readByte();*/
        }

        header->smpnum = numsamples;

    }

    for (i = 0; i < header->patnum; i++)
    {

        mp_ubyte* pattern = phead[i].patternData;
        mp_uint32 numrows = phead[i].rows;
        mp_uint32 numch = phead[i].channum;

        for (mp_uint32 r = 0; r < numrows; r++)
        {

            for (mp_uint32 c = 0; c < numch; c++)
            {

                mp_uint32 tnum = trackseq[i * 32 + c];
                if (tnum)
                {

                    mp_ubyte* track = tracks + ((tnum - 1) * 256 * 6) + r * 6;
                    //mp_ubyte *track=tracks[tnum-1]+r*6;

                    mp_uint32 pos = r * (numch * 8) + (c * 8);

                    if (track[0] <= 120) pattern[pos] = track[0];
                    else pattern[pos] = XModule::NOTE_OFF;

                    pattern[pos + 1] = track[1];
                    if (track[2])
                    {
                        if (pattern[pos] != XModule::NOTE_OFF)
                        {
                            pattern[pos + 2] = 0xC;
                            pattern[pos + 3] = cvol(track[2]);
                        }
                    }

                    mp_ubyte eff1 = track[3] & 0xf;
                    mp_ubyte eff2 = track[3] >> 4;

                    switch (eff1)
                    {
                        case 0x1: {
                            pattern[pos + 4] = 0x43;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x2: {
                            pattern[pos + 4] = 0x44;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x3: {
                            pattern[pos + 4] = 0x03;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x4: {
                            pattern[pos + 4] = 0x04;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x5: {
                            pattern[pos + 4] = 0x20;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x7: {
                            pattern[pos + 4] = 0x16;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0x8: {
                            pattern[pos + 4] = 0x08;
                            pattern[pos + 5] = cpan(track[4]);
                        }; break;
                        case 0xB: {
                            pattern[pos + 4] = 0x0B;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0xC: {
                            pattern[pos + 4] = 0x10;
                            pattern[pos + 5] = cvol(track[4]);
                        }; break;
                        case 0xD: {
                            pattern[pos + 4] = 0x0D;
                            pattern[pos + 5] = track[4];
                        }; break;
                        case 0xE: {
                            switch (track[4] >> 4)
                            {
                                case 0x1: {
                                    pattern[pos + 4] = 0x1E;
                                    pattern[pos + 5] = track[4];
                                }; break;
                                case 0x2: {
                                    pattern[pos + 4] = 0x1E;
                                    pattern[pos + 5] = track[4];
                                }; break;
                                case 0x6: {
                                    pattern[pos + 4] = 0x36;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0x7: {
                                    pattern[pos + 4] = 0x37;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0x9: {
                                    pattern[pos + 4] = 0x39;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0xA: {
                                    pattern[pos + 4] = 0x1E;
                                    pattern[pos + 5] = track[4];
                                }; break;
                                case 0xB: {
                                    pattern[pos + 4] = 0x1E;
                                    pattern[pos + 5] = track[4];
                                }; break;
                                case 0xC: {
                                    pattern[pos + 4] = 0x3C;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0xD: {
                                    pattern[pos + 4] = 0x3D;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0xE: {
                                    pattern[pos + 4] = 0x3E;
                                    pattern[pos + 5] = track[4] & 0xf;
                                }; break;
                                case 0xF: {
                                    pattern[pos + 4] = 0x1F;
                                    pattern[pos + 5] = track[5];
                                    pattern[pos + 6] = 0;
                                    pattern[pos + 7] = track[4] & 0xf;
                                }; break;
                            }
                        }; break;
                        case 0xF: {
                            pattern[pos + 4] = 0x1C;
                            pattern[pos + 5] = track[4];
                        }; break;
                        default: {
                            if (eff1)
                            {
                                //cprintf("Eff1: %i, %i\r\n",eff1,track[4]);
                                //getch();
                            }
                        }; break;
                    }

                    switch (eff2)
                    {
                        case 0x1: {
                            pattern[pos + 6] = 0x45;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0x2: {
                            pattern[pos + 6] = 0x46;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0x3: {
                            pattern[pos + 6] = 0x1B;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0x4: {
                            pattern[pos + 6] = 0x07;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0x7: {
                            pattern[pos + 6] = 0x16;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0x8: {
                            pattern[pos + 6] = 0x08;
                            pattern[pos + 7] = cpan(track[5]);
                        }; break;
                        case 0xB: {
                            pattern[pos + 6] = 0x0B;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0xC: {
                            pattern[pos + 6] = 0x10;
                            pattern[pos + 7] = cvol(track[5]);
                        }; break;
                        case 0xD: {
                            pattern[pos + 6] = 0x0D;
                            pattern[pos + 7] = track[5];
                        }; break;
                        case 0xE: {
                            switch (track[5] >> 4)
                            {
                                case 0x1: {
                                    pattern[pos + 6] = 0x1E;
                                    pattern[pos + 7] = track[5];
                                }; break;
                                case 0x2: {
                                    pattern[pos + 6] = 0x1E;
                                    pattern[pos + 7] = track[5];
                                }; break;
                                case 0x6: {
                                    pattern[pos + 6] = 0x36;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0x7: {
                                    pattern[pos + 6] = 0x37;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0x9: {
                                    pattern[pos + 6] = 0x39;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0xA: {
                                    pattern[pos + 6] = 0x1E;
                                    pattern[pos + 7] = track[5];
                                }; break;
                                case 0xB: {
                                    pattern[pos + 6] = 0x1E;
                                    pattern[pos + 7] = track[5];
                                }; break;
                                case 0xC: {
                                    pattern[pos + 6] = 0x3C;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0xD: {
                                    pattern[pos + 6] = 0x3D;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0xE: {
                                    pattern[pos + 6] = 0x3E;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                                case 0xF: {
                                    pattern[pos + 4] = 0x1F;
                                    pattern[pos + 5] = track[4];
                                    pattern[pos + 6] = 0;
                                    pattern[pos + 7] = track[5] & 0xf;
                                }; break;
                            }
                        }; break;
                        case 0xF: {
                            pattern[pos + 6] = 0x1C;
                            pattern[pos + 7] = track[5];
                        }; break;
                        default: {
                            if (eff2)
                            {
                                //cprintf("Eff2: %i, %i\r\n",eff2,track[5]);
                                //getch();
                            }
                        }; break;

                    }

                }

            }

        }

    }

    //cprintf("%i\r\n",phead[1].channum);

    //for (mp_sint32 r=0;r<64;r++) {
    //mp_sint32 o=(24*8)*r-2;
    //for (i=15;i<16;i++) {
    //	cprintf("n: %x, i: %x\r\n",phead[1].pattern[i*8+6+o],phead[1].pattern[i*8+7+o]);
    //   getch();
    //}
    //}

    delete[] tracks;
    delete[] trackseq;
    delete[] mdlins;
    delete[] mdlsamp;

    module->postProcessSamples();

    return 0;

}